package cn.maihe.elg.operation.security.auth.ajax;

import cn.maihe.elg.operation.centers.fuzhou.auth.FZAuthorization;
import cn.maihe.elg.operation.centers.fuzhou.auth.FZCenterAuthConfig;
import cn.maihe.elg.operation.centers.fuzhou.channel.service.FZApplyInfoService;
import cn.maihe.elg.operation.centers.fuzhou.config.FZMakeInfoConfig;
import cn.maihe.elg.operation.common.Constants;
import cn.maihe.elg.operation.model.enums.CenterNoEnum;
import cn.maihe.elg.operation.model.enums.UserType;
import cn.maihe.elg.operation.repository.entity.AcceptOrderInfoDO;
import cn.maihe.elg.operation.repository.entity.BidCustomerDO;
import cn.maihe.elg.operation.repository.entity.OrganizationDO;
import cn.maihe.elg.operation.repository.entity.entityCustom.UserDOCustom;
import cn.maihe.elg.operation.repository.mapper.BidCustomerMapper;
import cn.maihe.elg.operation.repository.mapper.UserMapper;
import cn.maihe.elg.operation.security.exceptions.UserLockedException;
import cn.maihe.elg.operation.security.model.UserContext;
import cn.maihe.elg.operation.service.business.BidCustomerService;
import cn.maihe.elg.operation.service.order.AcceptOrderInfoService;
import cn.maihe.elg.operation.service.system.OrganizationService;
import cn.maihe.elg.operation.service.system.UserService;
import cn.maihe.elg.operation.utils.RsaUtil;
import cn.maihe.elg.operation.utils.Sha256;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author vladimir.stankovic
 * <p>
 * Aug 3, 2016
 */
@Slf4j
@Component
public class AjaxAuthenticationProvider implements AuthenticationProvider {

    private final BCryptPasswordEncoder encoder;

    private final UserService userService;
    private final OrganizationService organizationService;

    private final FZApplyInfoService fzApplyInfoService;


    @Resource
    private UserMapper userMapper;
    @Resource
    protected FZMakeInfoConfig fzMakeInfoConfig;
    @Resource
    private BidCustomerMapper bidCustomerMapper;
    @Value("${allowFailedTimes}")
    private int allowFailedTimes;
    @Resource
    private AcceptOrderInfoService acceptOrderInfoService;
    @Resource
    private BidCustomerService bidCustomerService;

    @Autowired
    public AjaxAuthenticationProvider(final UserService userService, final BCryptPasswordEncoder encoder, final OrganizationService organizationService, FZApplyInfoService fzApplyInfoService) {
        this.userService = userService;
        this.encoder = encoder;
        this.organizationService = organizationService;
        this.fzApplyInfoService = fzApplyInfoService;
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        Assert.notNull(authentication, "No authentication data provided");


        String username = (String) authentication.getPrincipal();
        LoginRequest loginRequest = (LoginRequest) authentication.getCredentials();
        String socialCreditCode = "";
        String password = loginRequest.getPassword();
        if (loginRequest.getFzApplyParam() != null) {
            socialCreditCode = processRequest(loginRequest.getFzApplyParam());
            username = socialCreditCode;
            UserDOCustom user = userService.getByUsername(username);
            password = user.getPassword();
//            password = hashPassword(Constants.bidderDefaultPwd,socialCreditCode);
//            password = new BCryptPasswordEncoder().encode(Sha256.shaEncode("123456", socialCreditCode));
        }
        UserDOCustom user = userService.getByUsername(username);

//        String password = loginRequest.getPassword();
        if (user == null) {
            throw new UsernameNotFoundException("账号输入有误!");
        }

        if (userService.isUserLocked(user)) {
            throw new UserLockedException("用户被锁定");
        }

        if (user.getType() == UserType.BIDDER.getType()) {
            //获取并校验第三方token
            String thirdToken = loginRequest.getThirdToken();
            if (StringUtils.hasText(thirdToken)) {
                //todo:alan check thirdToken
            }
            String acceptOrderNo = loginRequest.getAcceptOrderNo();
            if (StringUtils.hasText(acceptOrderNo)) {
                if (!userService.checkOrder(user.getOrgId(), acceptOrderNo)) {
                    throw new UsernameNotFoundException("账号订单有误!");
                }
            } else {
                if (!StringUtils.isEmpty(password)) {
                    if (loginRequest.getFzApplyParam() != null) {
                        if (password.compareTo(user.getPassword()) != 0){
                            userService.loginError(user);
                            int loginFailedTimes = (user.getLoginFailTimes() == null ? 0 : user.getLoginFailTimes());
                            throw new BadCredentialsException("无效的用户名密码，剩余输入次数" + getResidueDegree(loginFailedTimes));
                        }
                    } else if (!encoder.matches(password, user.getPassword())) {
                        userService.loginError(user);
                        int loginFailedTimes = (user.getLoginFailTimes() == null ? 0 : user.getLoginFailTimes());
                        throw new BadCredentialsException("无效的用户名密码，剩余输入次数" + getResidueDegree(loginFailedTimes));
                    }
                } else {
                    int loginFailedTimes = (user.getLoginFailTimes() == null ? 0 : user.getLoginFailTimes());
                    throw new BadCredentialsException("无效的用户名密码，剩余输入次数" + getResidueDegree(loginFailedTimes));
                }
            }
        } else {
            if (!StringUtils.isEmpty(password)) {
                if (!encoder.matches(password, user.getPassword())) {
                    userService.loginError(user);
                    int loginFailedTimes = (user.getLoginFailTimes() == null ? 0 : user.getLoginFailTimes());
                    throw new BadCredentialsException("无效的用户名密码，剩余输入次数" + getResidueDegree(loginFailedTimes));
                }
            } else {
                int loginFailedTimes = (user.getLoginFailTimes() == null ? 0 : user.getLoginFailTimes());
                throw new BadCredentialsException("无效的用户名密码，剩余输入次数" + getResidueDegree(loginFailedTimes));
            }
            //todo:alan 暂不开启短信验证
//            else {
//                if (!userService.checkSmsCode(username, smsCode)) {
//                    throw new UsernameNotFoundException("短信验证有误!");
//                }
//            }
        }

        if (user.getRoleList() == null) {
            throw new InsufficientAuthenticationException("User has no roles assigned");
        }

        List<String> funcResource = userService.getUserPerm(user.getId());
        if (UserType.BIDDER.getType() == user.getType()) {
            if (funcResource.size() == 0) {
                funcResource.add("1");
            }
            funcResource.add("17");
            funcResource.add("16");
        }
        List<GrantedAuthority> authorities = funcResource.stream()
                .map(SimpleGrantedAuthority::new)
                .collect(Collectors.toList());

        Long bidderId = null;
        Long curOrgId = null;
        Integer bidderStatus = null;
        List<Long> centerIds = null;
        List<Long> institutionIds = null;
        String userId = String.valueOf(user.getId());
        String orgName = user.getFullName();
        String subAccount = null;
        if (UserType.BIDDER.getType() == user.getType()) {
            //投标客户 获取投标客户状态信息
            bidderId = user.getOrgId();
            BidCustomerDO bidCustomerDO = bidCustomerMapper.selectById(bidderId);

            // 判断 南平 龙岩 莆田交易中心是否需要补全客户资料
            String acceptOrderNo = loginRequest.getAcceptOrderNo();
            if (StringUtils.hasText(acceptOrderNo)) {
                AcceptOrderInfoDO acceptOrderInfoDO = acceptOrderInfoService.getByAcceptOrderNo(acceptOrderNo);
                subAccount = acceptOrderInfoDO.getSubAccount();
                Boolean result = bidCustomerService.isCompleteCustomerInfo(acceptOrderInfoDO, bidCustomerDO);
                if (result){
                    bidCustomerDO.setStatus(0);
                }
            }

            bidderStatus = bidCustomerDO.getStatus();
            orgName = bidCustomerDO.getBidderName();
        } else {
            OrganizationDO org = organizationService.getOrganizationByCode(user.getOrgCode());
            if (user.getType() == UserType.ACCOUNT.getType()) {
                centerIds = userService.getCenterIds(userId);
                institutionIds = userService.getInstitutionIds(userId);
            }
            curOrgId = org == null ? null : org.getId();
        }


        UserContext userContext = UserContext.create(userId, user.getUsername(), authorities,
                user.getOrgCode(), user.getType(), orgName, user.getName(), curOrgId, centerIds, institutionIds, user, bidderId, bidderStatus, subAccount);
        // 重置登录失败次数为0

        userService.resetLoginFailTimes(user.getId(), user.getLoginCount());
        return new UsernamePasswordAuthenticationToken(userContext, null, userContext.getAuthorities());
    }

    @Override
    public boolean supports(Class<?> authentication) {
        return (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication));
    }

    private int getResidueDegree(int loginFailedTimes) {
        return (allowFailedTimes - loginFailedTimes) < 0 ? 0 : (allowFailedTimes - loginFailedTimes);
    }

    private String processRequest(String param) {

        FZCenterAuthConfig authConfig = fzMakeInfoConfig.getAuthConfig(CenterNoEnum.FUZHOU);
        FZMakeInfoConfig.CenterInfo centerInfo = authConfig.getCenterInfo();


        // todo 测试用 加密数据
        /**
         * {
         *   "order_no" : "FZ20240909100250887",
         *   "return_url" : "https://miniwebtool.com/",
         *   "timestamp" : "20241015114423",
         *   "sign" : "7F3F5F6E763B3B937097F409FBB5628B"
         * }
         */
        String result = "JhTLSHW81SmAY84nPvNh0rtLEgMYqdHRbELGIxGqW%2FBrPINgwgeH38rXCQ5eDQoEcDykyTZ1%2FWQetpK2mak8qYtWGNVVyIkii%2B%2B6zbFTkSwHrJbkiz%2Bw3mxfGjVAJ6hOMcorVksC4ga%2B5UtblX%2FDyLy2" +
                "%2B2RPQHoad3iq%2BJsIgZutTlRTK7ygSK%2F8B83iX0vQ0HcAx4JArKELXEzxFe7fnttR0TlpMUeQrQdsaV7nPvO%2BJpm1JaErYKofTSK9Hcr7tDvOw54uzrFB1iPnk4BzkXacad140SeesuuyQsNVxZKkhSCHuc9OFtlqgd6Rw%2FZOyNqi7%2FYIznZe214HMV9kVg%3D%3D";

        String privateKeyPEM = centerInfo.getSm2PriKey();

//        byte[] privateKeyBytes = Base64.getDecoder().decode(privateKeyPEM);
//        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKeyBytes);


        String ciphertext = param;
        try {

            log.info("保函申请（同步跳转）请求参数原文:{}", ciphertext);
            // 解密
            String decryptedText = RsaUtil.decryptSegment(ciphertext, privateKeyPEM);
            log.info("保函申请（同步跳转）请求参数明文:{}", decryptedText);

            FZAuthorization fzAuthorization = authConfig.getFZAuthorization();
//            fzAuthorization.

//            System.out.println(decryptedText);
            // 需要验签
            Assert.isTrue(fzAuthorization.verifySignature(decryptedText), "验签失败");
//            Assert.isTrue(fzAuthorization.verifySignature("{\"order_no\":\"FZ202409109100250887\",\"sign\":\"622EB298FC46BF68786C8111B4C9A2CA\",\"return_url\":\"https://miniwebtool.com/\"," +
//                    "\"timestamp\":\"20241015114423\"}"));

            // 获取
            String socialCreditCode = fzApplyInfoService.requestApplyInfo(decryptedText);

            return socialCreditCode;
        } catch (IllegalArgumentException e) {
            throw new RuntimeException("验签失败");
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    public static String hashPassword(String password, String username) {

        String str = password + "{" + username + "}";

        // Java defaults to UTF-8, so if your input strings are already UTF-8,
        // this conversion is not needed. If they are in GBK, you'll need a library.

        // If you need GBK:
        // byte[] strBytes = str.getBytes("GBK"); // Requires iconv-lite equivalent

        // If your strings are already UTF-8, just use this:
        byte[] strBytes = str.getBytes(StandardCharsets.UTF_8); // UTF-8 Encoding

        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
        byte[] hashBytes = digest.digest(strBytes);

        // Convert to Hex and Uppercase
        String pwdSha = Hex.encodeHexString(hashBytes).toUpperCase();

        return pwdSha;
    }
}
